using System;
using System.Diagnostics;
using System.IO;
using System.Text;

namespace ZerAx.Net
{
    public class Packet
    {
        public const byte TYPE_DISCONNECT = 255;
        private static readonly byte[] MAGIC = new byte[] { (byte)'Z', (byte)'N', (byte)'T' };

        public byte typeId;
        private readonly MemoryStream data;
        public Packet(byte typeId)
        {
            this.typeId = typeId;
            data = new MemoryStream();
        }

        public Packet(byte typeId, MemoryStream data)
        {
            this.typeId = typeId;
            this.data = data;
        }

        public Packet(byte typeId, byte[] data)
        {
            this.typeId = typeId;
            this.data = new MemoryStream(data);
        }

        public Packet WriteData(Action<BinaryWriter> write)
        {
            data.Seek(0, SeekOrigin.End);
            write(new BinaryWriter(data, Encoding.UTF8));
            data.Seek(0, SeekOrigin.Begin);
            return this;
        }

        public void ReadData(Action<BinaryReader> read)
        {
            read(new BinaryReader(data));
        }

        public T ReadData<T>(Func<BinaryReader, T> read)
        {
            return read(new BinaryReader(data, Encoding.UTF8));
        }

        public void Write(Stream stream)
        {
            var writer = new BinaryWriter(stream);
            writer.Write(MAGIC);
            writer.Write(typeId);
            var dataArr = data.ToArray();
            writer.Write(dataArr.Length);
            writer.Write(dataArr);
            writer.Flush();
        }

        public static Packet From(Stream stream)
        {
            if (!stream.CanRead) return null;
            using var reader = new BinaryReader(stream);
            int i = 0;
            while (i < MAGIC.Length)
            {
                if (!reader.BaseStream.CanRead) return null;

                var b = reader.ReadByte();
                if (b != MAGIC[i])
                {
                    i = 0;
                }
                else
                {
                    i += 1;
                }
            }
            byte typeId = reader.ReadByte();
            int dataLen = reader.ReadInt32();
            byte[] data = new byte[dataLen];
            int len = reader.Read(data, 0, dataLen);

            return new Packet(typeId, data[..len]);
        }

        public byte[] GetBytes()
        {
            using var stream = new MemoryStream();
            Write(stream);
            return stream.ToArray();
        }



        private static int? FindHeaderBeginning(byte[] data)
        {
            int i = 0, j = 0;
            while (i < data.Length && j < MAGIC.Length)
            {
                if (data[i] != MAGIC[j])
                {
                    j = 0;
                }
                else
                {
                    j += 1;
                }
                i += 1;
            }

            if (i >= data.Length) return null;

            return i;
        }
    }
}